/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "ola_pal_os.h"

#include <stdlib.h>
#include <securec.h>
#include <pthread.h>
#include <ola_log.h>
#include <semaphore.h>
#include <stdarg.h>

#define MS_PER_SECOND 1000
#define US_PER_MS 1000
#define NS_PER_MS 1000000
#define NS_PER_SECOND 1000000000UL
#define LONG_MAX 0x7fffffffL

#define OsLog(...) \
    do { \
        printf("[OLA_PAL_OS][%s][%d]", __func__, __LINE__); \
        printf(__VA_ARGS__); \
        printf("\r\n"); \
    } while (0)

/*
 * 功能: 申请内存空间
 * 参数: size, 申请内存的大小
 * 返回: 分配的内存空间指针
 */
void *OlaMalloc(unsigned int size)
{
    void *ptr = NULL;
    if (size > 0) {
        ptr = malloc(size);
    }
    return ptr;
}

/*
 * 功能: 释放申请的内存空间
 * 参数: ptr, 指向内存空间的指针
 */
void OlaFree(void *ptr)
{
    if (ptr != NULL) {
        free(ptr);
    }
    return;
}

/*
 * 功能: 创建任务
 * 参数: func, 任务执行函数
 *       param, 任务执行函数入参
 *       taskParam, 任务相关参数
 * 返回: NULL 失败
 *       0xFFFF 成功
 */
OlaTask OlaTaskCreate(OlaTaskFunc func, void *param, const OlaTaskParam *taskParam)
{
    if ((func == NULL) || (taskParam == NULL)) {
        OsLog("invalid param");
        return NULL;
    }
    int err;
    pthread_attr_t attr;
    pthread_t tid;
    static unsigned int handle = 0;
    OlaTask ret = (OlaTask)&handle;

    err = pthread_attr_init(&attr);
    if (err != 0) {
        OsLog("pthread_attr_init failed, ret[%d]", err);
        return NULL;
    }

    err = pthread_attr_setstacksize(&attr, taskParam->stackSize);
    if (err != 0) {
        OsLog("pthread set stack size attr failed, size[%u] ret[%d]", taskParam->stackSize, err);
        goto EXIT;
    }

    err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    if (err != 0) {
        OsLog("pthread set detached attr failed, ret[%d]", err);
        goto EXIT;
    }

    err = pthread_create(&tid, &attr, (void *)func, param);
    if (err != 0) {
        OsLog("pthread_create failed, ret[%d]", err);
    }

EXIT:
    if (err != 0) {
        ret = NULL;
    }

    err = pthread_attr_destroy(&attr);
    if (err != 0) {
        OsLog("destroy pthread attr failed, ret[%d]", err);
    }

    return ret;
}

/*
 * 功能: 删除任务
 * 参数: handle, 任务句柄
 * 返回: 0, 表示成功
 *       -1, 表示失败
 */
int OlaTaskDelete(OlaTask handle)
{
    (void)handle;

    /*
     * 注意：posix下不适配线程删除接口，线程创建时属性设置为detached，
     * 线程应该在达到某种条件时自然退出
     */
    return 0;
}

/*
 * 功能: 获取当前任务句柄
 * 返回: 当前任务句柄
 */
OlaTask OlaTaskGetCurTaskId(void)
{
    return (OlaTask)pthread_self();
}

/*
 * 功能: 将任务挂起暂停
 * 参数: handle, 任务句柄
 * 返回: 0, 表示成功
 *       -1, 表示失败
 */
int OlaTaskSuspend(const OlaTask handle)
{
    (void)handle;
    return 0;
}

/*
 * 功能: 将任务恢复唤醒
 * 参数: handle, 任务句柄
 * 返回: 0, 表示成功
 *       -1, 表示失败
 */
int OlaTaskResume(const OlaTask handle)
{
    (void)handle;
    return 0;
}

/*
 * 功能: 获取任务的运行状态
 * 参数: handle, 任务句柄
 * 返回: 任务当前的运行状态
 */
OlaTaskStuts OlaTaskGetState(const OlaTask handle)
{
    (void)handle;
    return OLA_TASK_ERROR;
}

/*
 * 功能: 创建互斥锁
 * 返回: mutex句柄
 */
OlaMutex OlaMutexCreate(void)
{
    int ret;
    pthread_mutex_t *mutex = NULL;

    mutex = OlaMalloc(sizeof(pthread_mutex_t));
    if (mutex == NULL) {
        OsLog("malloc failed");
        return NULL;
    }

    ret = pthread_mutex_init(mutex, NULL);
    if (ret != 0) {
        OlaFree(mutex);
        OsLog("mutex init failed, ret[%d]", ret);
        return NULL;
    }

    return mutex;
}

/*
 * 功能: 销毁互斥锁
 * 参数: mutex, 互斥锁句柄
 * 返回: 0, 表示成功
 *       -1, 表示失败
 */
int OlaMutexDestroy(OlaMutex mutex)
{
    int ret;

    if (mutex == NULL) {
        OsLog("invalid param");
        return -1;
    }

    ret = pthread_mutex_destroy((pthread_mutex_t *)mutex);
    if (ret != 0) {
        OsLog("mutex destroy failed, ret[%d]", ret);
        return -1;
    }

    OlaFree(mutex);
    mutex = NULL;
    return 0;
}

/*
 * 功能: 获取互斥锁
 * 参数: mutex, 互斥锁句柄
 *       timeout, 等待超时时间, 值为0xFFFFFFFF时不超时
 * 返回: 0, 表示成功
 *       -1, 表示失败
 */
int OlaMutexLock(OlaMutex mutex, unsigned int timeout)
{
    int ret;
    (void) timeout; /* linux不使用该参数 */

    if (mutex == NULL) {
        OsLog("invalid param");
        return -1;
    }

    ret = pthread_mutex_lock((pthread_mutex_t *)mutex);
    if (ret != 0) {
        OsLog("lock failed, ret[%d]", ret);
        return -1;
    }

    return 0;
}

/*
 * 功能: 释放互斥锁
 * 参数: mutex, 互斥锁句柄
 * 返回: 0, 表示成功
 *       -1, 表示失败
 */
int OlaMutexUnLock(OlaMutex mutex)
{
    int ret;

    if (mutex == NULL) {
        OsLog("invalid param");
        return -1;
    }

    ret = pthread_mutex_unlock((pthread_mutex_t *)mutex);
    if (ret != 0) {
        OsLog("unlock failed, ret[%d]", ret);
        return -1;
    }

    return 0;
}

/*
 * 功能: 创建信号量
 * 参数: initCount, 信号量初始值
 * 返回: 信号量句柄
 */
OlaSem OlaSemCreate(unsigned int initCount)
{
    int ret;
    sem_t *tmpSem = (sem_t *)OlaMalloc(sizeof(sem_t));
    if (tmpSem == NULL) {
        OsLog("malloc failed");
        return NULL;
    }

    /* 第二个参数为0表示信号量只在同一进程内的线程间共享 */
    ret = sem_init(tmpSem, 0, initCount);
    if (ret != 0) {
        OlaFree(tmpSem);
        OsLog("sem init failed, ret[%d]", ret);
        return NULL;
    }

    return tmpSem;
}

/*
 * 功能: 销毁信号量
 * 参数: handle, 信号量句柄
 * 返回: 0, 表示成功
 *       -1, 表示失败
 */
int OlaSemDestroy(OlaSem handle)
{
    if (handle == NULL) {
        OsLog("invalid param");
        return -1;
    }

    sem_destroy((sem_t *)handle);
    OlaFree(handle);
    return 0;
}

/*
 * 功能: 释放信号量
 * 参数: handle, 信号量句柄
 * 返回: 0, 表示成功
 *       -1, 表示失败
 */
int OlaSemPost(OlaSem handle)
{
    if (handle == NULL) {
        OsLog("invalid param");
        return -1;
    }

    errno = 0;
    int ret = sem_post((sem_t *)handle);
    if (ret != 0) {
        OsLog("sem post failed, errno[%d]", errno);
        return -1;
    }

    return 0;
}

/*
 * 功能: 获取信号量
 * 参数: handle, 信号量句柄
 *       timeout, 等待超时时间
 * 返回: 0, 表示成功
 *       -1, 表示失败
 */
int OlaSemAcquire(OlaSem handle, unsigned int timeout)
{
    if (handle == NULL) {
        return -1;
    }

    struct timespec ts;
    if (clock_gettime(CLOCK_REALTIME, &ts) != 0) {
        OsLog("get time fail in semaphore wait\n");
        return -1;
    }

     /* 等待timeoutMs毫秒后会超时返回 */
    ts.tv_sec += timeout / MS_PER_SECOND;
    /* 纳秒可能会溢出，导致sem_wait不阻塞，立即返回 */
    long nsec = (long)timeout % MS_PER_SECOND * NS_PER_MS;
    if (ts.tv_nsec >= (LONG_MAX - nsec)) {
        ts.tv_nsec -= (LONG_MAX - nsec);
        ts.tv_sec += (LONG_MAX % NS_PER_SECOND);
    } else {
        ts.tv_nsec += nsec;
    }

    int ret = sem_timedwait((sem_t *)handle, &ts);
    if (ret != 0) {
        return -1;
    }
    return 0;
}

/*
 * 功能: 毫秒级休眠
 * 参数: ms, 休眠时间
 */
void OlaSleepMs(unsigned int ms)
{
    int ret;
    struct timeval tm;
    tm.tv_sec = ms / MS_PER_SECOND;
    tm.tv_usec = (ms % MS_PER_SECOND) * US_PER_MS;

    do {
        ret = select(0, NULL, NULL, NULL, &tm);
    } while ((ret == -1) && (errno == EINTR));

    return;
}

/*
 * 功能：获取系统运行时间
 * 参数：ms存储系统时间的缓冲区
 * 返回：0表示成功，返回其他失败
 */
int OlaGetTime(unsigned long long *ms)
{
    struct timespec ts;

    if (ms == NULL) {
        return -1;
    }

    (void)memset_s(&ts, sizeof(ts), 0, sizeof(ts));

    /* 获取失败 */
    if (clock_gettime(1, &ts) != 0) {
        return -1;
    }

    /* 转换为毫秒，tv_sec为秒乘以1000为毫秒，tv_nsec单位为纳秒除以1000000为毫秒 */
    *ms = (unsigned long long)(ts.tv_sec * MS_PER_SECOND + ts.tv_nsec / NS_PER_MS);

    return 0;
}

/*
 * 功能：日志打印接口
 * 参数：format为格式字符，后面为可变参数
 * 返回：打印字符数
 */
int OlaPrintf(const char *format, ...)
{
    if (format == NULL) {
        return 0;
    }

    va_list ap;
    va_start(ap, format);
    int ret = vprintf(format, ap);
    va_end(ap);

    return ret;
}
